#include "vmidi_impl_common.h"
#include <iostream>
using namespace std;

namespace VMidi {
  
int VMidiImplCommon::
addPort (void* port, VMidiPortType type) {
  int id = getId();
  if (type == VMIDI_PORT_INPUT) {
   inputs.insert(pair<int,void*>(id, port));
  }
  else if (type == VMIDI_PORT_OUTPUT) {
    outputs.insert(pair<int,void*>(id, port));
  }
  return id;
}

void* VMidiImplCommon::
removePort (const int id, VMidiPortType type) throw(string) {
  map<int,void*> *pmap = NULL;

  if (type == VMIDI_PORT_INPUT) {
    checkPortId(VMIDI_PORT_INPUT, id);
    pmap = &inputs;

    // Delete connections starting from the input port.
    map< int,set<int>* >::iterator it = connections.find(id);
    if (it != connections.end()) {
      delete it->second;
      connections.erase(id);
    }
  }
  else if (type == VMIDI_PORT_OUTPUT) {
    pmap = &outputs;
    checkPortId(VMIDI_PORT_OUTPUT, id);

    // Delete connections ending with the output port.
    for (map< int,set<int>* >::iterator it = connections.begin();
        it != connections.end();
        ++it) {
      int srcPortId = it->first;
      set<int>* outputs = it->second;
      bool found = false;
      for (set<int>::iterator s_it = outputs->begin();
          s_it != outputs->end();
          ++s_it) {
        if (*s_it == id) {
          found = true;
          break;
        }
      }
      if (found) {
        outputs->erase(id);
        // No more connections for the input : free the heap space and remove the
        // entry from the map.
        if (outputs->empty()) {
          connections.erase(srcPortId);
          delete outputs;
        }
      }
    }
  }
    
  if (pmap != NULL) {
    map<int,void*>::iterator it = pmap->find(id);
    // Found.
    if (it != inputs.end()) {
      void* port = it->second;
      // Remove the port from the map
      (*pmap).erase(it);
      // Add its id to the id pool
      idPool.push_back(id);
      // Return it for external deletion by client code.
      return port;
    }
  }

  return NULL;
}


void VMidiImplCommon::
connectPorts (int srcPortId, int dstPortId) throw(string) {
  checkPortId(VMIDI_PORT_INPUT, srcPortId);
  checkPortId(VMIDI_PORT_OUTPUT, dstPortId);

  if (srcPortId == dstPortId) {
    throw "Self-connections are not allowed";
  }

  map< int, set<int>* >::iterator it;

  it = connections.find(srcPortId);

  // No connection for srcPortId yet.
  if (it == connections.end()) {
    set<int>* outputs = new set<int>;
    outputs->insert(dstPortId);
    connections[srcPortId] = outputs;
  }
  else {
    it->second->insert(dstPortId);
  }
}

void VMidiImplCommon::
disconnectPorts (int srcPortId, int dstPortId) throw(string) {
  checkPortId(VMIDI_PORT_INPUT, srcPortId);
  checkPortId(VMIDI_PORT_OUTPUT, dstPortId);

  if (srcPortId == dstPortId) {
    throw "Self-connections are not allowed so disconnection would do nothing.";
  }

  map< int, set<int>* >::iterator it;

  it = connections.find(srcPortId);

  if (it == connections.end()) {
    stringstream ss;
    ss << "disconnectPorts() : input port : " << srcPortId;
    ss << " has no connection.";
    throw ss.str();
  }

  set<int>* outputs = it->second;
  bool found = false;
  for (set<int>::iterator s_it = outputs->begin();
       s_it != outputs->end();
       ++s_it) {
    if (*s_it == dstPortId) {
      found = true;
      break;
    }
  }

  if (found) {
    outputs->erase(dstPortId);
    // No more connections for the input : free the heap space and remove the
    // entry from the map.
    if (outputs->empty()) {
      connections.erase(srcPortId);
      delete outputs;
    }
  }
  else {
    stringstream ss;
    ss << "disconnectPorts() : no connection with output port: " << dstPortId;
    throw ss.str();
  }

}

void VMidiImplCommon::
finalize () throw(string) {
  // Destroys the memory allocated on the heap for the connections.
  for (map< int,set<int>* >::iterator it = connections.begin();
       it != connections.end();
       ++it) {
    delete it->second;
  }
}

void VMidiImplCommon::
dump () {
  cout << "Virtual input ports :" << endl;
  for (map<int, void*>::iterator it = inputs.begin();
       it != inputs.end();
       ++it) {
    int id = it->first;
    void* dat = it->second;
    cout << " (" << id << ") : " << dat << endl;
  }

  cout << endl << "Virtual output ports :" << endl;
  for (map<int, void*>::iterator it = outputs.begin();
       it != outputs.end();
       ++it) {
    int id = it->first;
    void* dat = it->second;
    cout << " (" << id << ") : " << dat << endl;
  }

  cout << endl << "Connections (THRU) :" << endl;
  for (map< int,set<int>* >:: iterator it = connections.begin();
       it != connections.end();
       ++it) {
    int id = it->first;
    set<int>* outputs = it->second;

    cout << " (" << id << ") :";
    
    for (set<int>::iterator s_it = outputs->begin();
         s_it != outputs->end();
         ++s_it) {
      cout << " " << *s_it;
    }

    cout << endl;
  }
}

};

